package cn.ad.server.job.adapter.persistence.job;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.Resource;

import cn.ad.server.job.adapter.dbaccess.AssembledCachedDataContainer;
import cn.ad.server.job.domain.job.StrategyRepositoryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import cn.ad.server.persistence.dao.CampaignDao;
import cn.ad.server.persistence.dao.DayPartingDao;
import cn.ad.server.persistence.dao.StrategyDao;
import cn.ad.server.job.adapter.persistence.job.StrategyWarmUpServant.StrategyGoupMap;
import cn.ad.server.domain.ad.Campaign;
import cn.ad.server.domain.ad.DayTargert;
import cn.ad.server.domain.ad.Strategy;

@Repository
public class StrategyRepositoryServent implements StrategyRepositoryService {

	private static Logger logger = LoggerFactory.getLogger(StrategyRepositoryServent.class);

	public final static int STATUS_VALID = 1;
	/**
	 * 24小时时间位
	 */
	private static int[] masks = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
			131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608 };

	@Autowired
	private StrategyDao strategyDao;

	@Autowired
	private CampaignDao campaignDao;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, String> stringValueOperations;

	@Resource(name = "cacheRedisTemplate")
	private ValueOperations<String, Strategy> strategyOperations;

	@Resource(name = "countRedisTemplate")
	private ValueOperations<String, Double> countOperations;

	@Resource(name = "cacheRedisTemplate")
	private RedisTemplate<String, String> cacheRedisTemplate;

	@Resource(name = "countRedisTemplate")
	private RedisTemplate<String, String> countRedisTemplate;


	/**
	 * 时间定向。匹配单位为小时。匹配周期为一周。 参数 date为null 则为当前时间。
	 * 
	 * @param strategy
	 * @param date
	 * @return
	 */
	boolean matchDayTarget(Strategy strategy, Date date) {
		DayTargert dayTargert = strategy.getDayTargert();
		if (strategy == null || strategy.getDayTargert() == null) {
			throw new IllegalArgumentException("org strategy illegal ");
		}
		Calendar calendar = Calendar.getInstance();
		if (date != null) {
			calendar.setTime(date);
		}
		int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;
		int hour = calendar.get(Calendar.HOUR_OF_DAY);
		boolean validated = isTargetingTimeInternalRange(dayOfWeek, hour, dayTargert.toArray());
		return validated;
	}

	/**
	 * 配合方法 matchDayTarget(Strategy strategy, Date date)
	 * 
	 * @param dayOfWeek
	 * @param hour
	 * @param ranges
	 * @return
	 */
	boolean isTargetingTimeInternalRange(int dayOfWeek, int hour, Integer[] ranges) {
		return (ranges[dayOfWeek] & masks[hour]) != 0 ? true : false;
	}


	@Autowired
	private AssembledCachedDataContainer container;

	private ConcurrentHashMap<Integer, StrategyGoupMap> mapStrategyGroupAll;

	/**
	 * 第一次加载所有。以后只加载变化的数据。
	 */
	@Override
	public void warmUpStrategy() {
		this.mapStrategyGroupAll = container.getMapStrategyGroupAll();
		//String warmUpTime = stringValueOperations.get(Constants.WARM_UP_TIME);
		List<Strategy> strategys = null;
		strategys = strategyDao.findAll2();
		for (Strategy s : strategys) {
			try {
				if (s.getStatus() == 1) {
					s = attachCampaign(s);
					addDayTarget(s);
					attachGroup(s);
					strategyOperations.set(s.toCacheKey(), s);
				} else if (s.getStatus() != 1) {
					cacheRedisTemplate.delete(s.toCacheKey());
				}
			} catch (Exception e) {
				if (s != null) {
					logger.error("错误：在获取strategy_id =" + s.getId() + "数据时发生错误 \n 错误信息：", e);
				}
			}
		}
	}

	/**
	 * 附上策略组
	 * 
	 * @param strategy
	 */
	private void attachGroup(Strategy strategy) {
		if (strategy == null) {
			return;
		}
		int strategyGroupId = strategy.getStrategyGroupId();
		if (strategyGroupId != 0) {
			StrategyGoupMap strategyGoupMap = mapStrategyGroupAll.get(strategyGroupId);
			if (strategyGoupMap != null) {
				strategy.setStrategyGroup(strategyGoupMap.getGroup());
			}
		}

	}

	/**
	 * 附上Campaign对象
	 * 
	 * @param strategy
	 * @return
	 */
	private Strategy attachCampaign(Strategy strategy) {

		if (strategy == null) {
			return null;
		}

		Campaign campaign = strategy.getCampaign();
		if (campaign != null) {
			Campaign _campaign = campaignDao.selectById(campaign.getId(), STATUS_VALID);
			strategy.setCampaign(_campaign);
		}
		return strategy;
	}

	@Autowired
	private DayPartingDao dayPartingDao;

	/**
	 * 添加时间定向。 FIXME 在adSpaceRepoositoryImpl中有重复
	 * 
	 * @param strategy
	 */
	private void addDayTarget(Strategy strategy) {
		if (strategy == null) {
			return;
		}
		/**
		 * 时间定向
		 */
		List<DayTargert> dayTargerts = dayPartingDao.selectAllByStrategyId(strategy.getId(), 1);
		if (dayTargerts.size() > 0) {
			DayTargert dayTargert = dayTargerts.get(0);

			if (dayTargert.getMonday() == 0 && dayTargert.getTuesday() == 0 && dayTargert.getWednesday() == 0
					&& dayTargert.getThursday() == 0 && dayTargert.getFriday() == 0 && dayTargert.getSaturday() == 0
					&& dayTargert.getSunday() == 0) {
				int allHours = 16777215;
				dayTargert.setMonday(allHours);
				dayTargert.setTuesday(allHours);
				dayTargert.setWednesday(allHours);
				dayTargert.setThursday(allHours);
				dayTargert.setFriday(allHours);
				dayTargert.setSaturday(allHours);
				dayTargert.setSunday(allHours);
				strategy.setDayTargert(dayTargert);
			}
			strategy.setDayTargert(dayTargert);
		} else {
			// 如果没有则默认一周这个时间段全投放
			DayTargert dayTargert = new DayTargert();
			int allHours = 16777215;
			dayTargert.setMonday(allHours);
			dayTargert.setTuesday(allHours);
			dayTargert.setWednesday(allHours);
			dayTargert.setThursday(allHours);
			dayTargert.setFriday(allHours);
			dayTargert.setSaturday(allHours);
			dayTargert.setSunday(allHours);
			strategy.setDayTargert(dayTargert);
		}
	}

	
	/**
	 * 计算小时数。 start >=0 end <=23
	 * 
	 * @param start
	 * @param end
	 * @param src
	 * @return
	 */
	public Integer computeHoursBetween(int startHour, long endHour, int src) {
		int exceptHours = 0;
		for (long i = startHour; i <= endHour; i++) {
			if ((src & (1 << i)) == 0) {
				exceptHours++;
			}
		}
		return exceptHours;
	}


}
